package com.credithc.eagleeye.plugins.dubbo;
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcInvocation;
import com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation;
import com.credithc.eagleeye.agent.factory.EagleEyeAgentFactory;
import com.credithc.eagleeye.constants.RpcTypeEnum;
import com.credithc.eagleeye.exception.EagleEyeIgnoreException;
import com.credithc.eagleeye.plugins.util.LogUtil;
import com.credithc.eagleeye.thread.EagleEyeThreadContext;
import com.credithc.eagleeye.thread.TraceInfo;
import com.credithc.eagleeye.util.FastJsonConvert;
import com.credithc.eagleeye.util.ThrowableToStringArray;
import com.credithc.eagleeye.vo.RpcTraceInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("Duplicates")
public class EagleEyeDubboFilter implements Filter {
    public static final Logger logger= LoggerFactory.getLogger(EagleEyeDubboFilter.class);
    private static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");

    public EagleEyeDubboFilter() {
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (invocation instanceof DecodeableRpcInvocation) {
            String oldTraceId = invocation.getAttachment("traceId");
            String oldRpcId = invocation.getAttachment("rpcId");
            String clientName = invocation.getAttachment("clientName");
            String clientIp = RpcContext.getContext().getRemoteAddress().getHostString();
            @SuppressWarnings("unchecked")
            Map<String, String> userInfo = FastJsonConvert.convertJSONToObject(invocation.getAttachment("userInfo"), Map.class);
            EagleEyeThreadContext.init();
            TraceInfo traceInfo = null;
            //将客户端传过来的调用ID记录下来
            if (StringUtils.isNotBlank(oldTraceId) && StringUtils.isNotBlank(oldRpcId)) {
                traceInfo = new TraceInfo(oldTraceId, oldRpcId, userInfo);
            } else {
                //如果没有，则视为第一层调用，新生成一个调用信息
                traceInfo = new TraceInfo();
            }
            long beginTime = System.currentTimeMillis();
            RpcTraceInfoVo rpcTraceInfoVo = null;
            try {
                rpcTraceInfoVo = createRpcTraceInfo(invoker, invocation, traceInfo.getTraceId(), traceInfo.getRpcId(), clientName, userInfo,false);
                //增加一层次
                traceInfo.addHierarchy();
                EagleEyeThreadContext.putTraceInfo(traceInfo);
                MDC.put("traceId",traceInfo.getTraceId());
                MDC.put("rpcId",traceInfo.getRpcId());
                MDC.put("clientIp",clientIp);
                //执行调用
                Result result = invoker.invoke(invocation);
                //填充鹰眼信息
                fillRpcTraceInfo(rpcTraceInfoVo, beginTime, result);
                return result;
            } catch (Exception ex) {
                //ERROR
                rpcTraceInfoVo.setResult("ERROR");
                throw ex;
            } finally {
                try {
                    MDC.remove("traceId");
                    MDC.remove("rpcId");
                    MDC.remove("clientIp");
                    EagleEyeAgentFactory.getInstance().send2EagleEye(rpcTraceInfoVo);
                    LogUtil.printSplunkLog(rpcTraceInfoVo);
                } catch (Exception ex) {
                    logger.error("dubbo send2EagleEye error:"+ex.getMessage());
                }
            }
        } else if (invocation instanceof RpcInvocation) {
            TraceInfo traceInfo = EagleEyeThreadContext.getTraceInfo();
            if (traceInfo == null) {
                traceInfo = new TraceInfo();
            } else {
                traceInfo.addSequenceNo();
            }
            Map<String, String> attachments = new HashMap<>();
            attachments.put("traceId", traceInfo.getTraceId());
            attachments.put("rpcId", traceInfo.getRpcId());
            attachments.put("userInfo", FastJsonConvert.convertObjectToJSON(traceInfo.getUserInfo()));
            attachments.put("clientName", EagleEyeAgentFactory.getInstance().getEagleEyeAgentConfig().getAgentClientName());
            RpcInvocation rpcInvocation = (RpcInvocation) invocation;
            rpcInvocation.addAttachments(attachments);

            //HttpServletRequest httpServletRequest = RpcContext.getContext().getRequest(HttpServletRequest.class);
            String protocol = rpcInvocation.getInvoker().getUrl().getProtocol();
            boolean isRestInvoke= StringUtils.equals(protocol,"rest");
            //rest调用直接发鹰眼
            if(isRestInvoke) {
                RpcTraceInfoVo rpcTraceInfoVo = null;
                try {
                    rpcTraceInfoVo = createRpcTraceInfo(invoker, invocation, traceInfo.getTraceId(), traceInfo.getRpcId(), EagleEyeAgentFactory.getInstance().getEagleEyeAgentConfig().getAgentClientName(), traceInfo.getUserInfo(),true);
                    //增加一层次
                    traceInfo.addHierarchy();
                    EagleEyeThreadContext.putTraceInfo(traceInfo);
                    MDC.put("traceId", traceInfo.getTraceId());
                    MDC.put("rpcId", traceInfo.getRpcId());
                    long beginTime = System.currentTimeMillis();
                    //执行调用
                    Result result = invoker.invoke(invocation);
                    //填充鹰眼信息
                    fillRpcTraceInfo(rpcTraceInfoVo, beginTime, result);
                    return result;
                } catch (Exception ex) {
                    rpcTraceInfoVo.setResult("ERROR");
                    throw ex;
                }finally {
                    try {
                        MDC.remove("traceId");
                        MDC.remove("rpcId");
                        MDC.remove("clientIp");
                        EagleEyeAgentFactory.getInstance().send2EagleEye(rpcTraceInfoVo);
                        LogUtil.printSplunkLog(rpcTraceInfoVo);
                    } catch (Exception ex) {
                        logger.error("dubbo send2EagleEye error:" + ex.getMessage());
                    }
                }
            }else{
                //dubbo调用等待DecodeableRpcInvocation处理
                return invoker.invoke(invocation);
            }
        }
        return invoker.invoke(invocation);
    }

    public RpcTraceInfoVo createRpcTraceInfo(Invoker<?> invoker, Invocation invocation,String traceId,String rpcId,String clientName,Map<String,String> userInfo,boolean isRestInvoke) {
        RpcTraceInfoVo rpcTraceInfoVo = new RpcTraceInfoVo();
        String dubboUrl = invoker.getUrl().toString();
        String applicationName = StringUtils.substringBetween(dubboUrl, "application=", "&");
        String serverHost = StringUtils.substringBetween(dubboUrl, "dubbo://", "/");
        rpcTraceInfoVo.setClientHost(RpcContext.getContext().getRemoteAddress().getHostString());
        rpcTraceInfoVo.setClientName(clientName);
        rpcTraceInfoVo.setRequestDateTime(ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS.format(new Date()));
        rpcTraceInfoVo.setTraceId(traceId);
        rpcTraceInfoVo.setRpcId(rpcId);
        if(isRestInvoke){
            rpcTraceInfoVo.setRpcType(RpcTypeEnum.DUBBO.getRpcName()+"-"+RpcTypeEnum.REST.getRpcName());
        }else{
            rpcTraceInfoVo.setRpcType(RpcTypeEnum.DUBBO.getRpcName());
        }
        rpcTraceInfoVo.setServiceCategory(applicationName);
        rpcTraceInfoVo.setServiceName(invoker.getInterface().getSimpleName());
        rpcTraceInfoVo.setMethodName(invocation.getMethodName());
        rpcTraceInfoVo.setRequestJson(FastJsonConvert.convertObjectToJSON(invocation.getArguments()));
        rpcTraceInfoVo.setUserInfo(userInfo);
        rpcTraceInfoVo.setServerHost(serverHost);
        return rpcTraceInfoVo;
    }

    public void fillRpcTraceInfo(RpcTraceInfoVo rpcTraceInfoVo,long beginTime,Result result) {
        rpcTraceInfoVo.setRunTime(System.currentTimeMillis() - beginTime);
        Throwable exception = result.getException();
        if (exception != null && !(exception instanceof EagleEyeIgnoreException)) {
            //ERROR
            rpcTraceInfoVo.setResult("ERROR");
            //异常的结果
            String traceStr[] = ThrowableToStringArray.convert(exception);
            StringBuilder builder = new StringBuilder();
            for(String trace : traceStr) {
                builder.append(trace);
                builder.append("\n");
            }
            rpcTraceInfoVo.setResponseJson(builder.toString());
        } else {
            //OK
            rpcTraceInfoVo.setResult("OK");
            rpcTraceInfoVo.setResponseJson(FastJsonConvert.convertObjectToJSON(result.getValue()));
        }
    }
}
